﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Reflection;
using UmlControlLibrary.Tools;

namespace UmlControlLibrary
{
    public class UmlProperty : UmlMemberElement, ICloneable
    {
        public enum ArrayType
        {
            Array,
            List
        }
        public new class Events
        {
            public static Event Created = new Event(Event.Created, typeof(UmlProperty));
            public static Event Modified = new Event(Event.Modified, typeof(UmlProperty));
            public static Event CardinalityModified = new Event("Cardinality"+Event.Modified, typeof(UmlProperty));
            public static Event Deleted = new Event(Event.Deleted, typeof(UmlProperty));
        }

        public UmlElement Owner
        {
            get;
            set;
        }

        public static bool isArray(string type)
        {
            return ((type.Contains('[') && type.EndsWith("]"))
                || (type.StartsWith("List<",StringComparison.OrdinalIgnoreCase) && type.EndsWith(">"))
                );
        }

        public static ArrayType? getArrayKind(ref string type, ref int? size)
        {
            if (type.Contains('[') && type.EndsWith("]"))
            {
                int pos = type.IndexOf('[') + 1;
                string temp = type.Substring(pos, type.Length - (pos + 1));
                int s;
                if (int.TryParse(temp, out s))
                    size = s;
                else
                    size = null;

                type = type.Substring(0, pos - 1);
                return ArrayType.Array;
            }
            else if (type.StartsWith("List<", StringComparison.OrdinalIgnoreCase) && type.EndsWith(">"))
            {
                int pos = type.IndexOf('<') + 1;
                type = type.Substring(pos, type.Length-(pos + 1));
                size = null;
                return ArrayType.List;
            }
            else 
                return null;
        }

        public override string ElementLabel
        {
            get
            {
                string l = "";
                if (StereoType.Length > 0)
                    l = "« " + StereoType + " » ";
                if ((Modifiers & UmlModifier._public) != 0)
                        l += "+";
                if ((Modifiers & UmlModifier._private)!=0)
                        l += "-";
                if ((Modifiers & UmlModifier._protected)!=0)
                        l += "#";

                if (DerivationExpression.Length > 0)
                    l += "/";

                string typeLabel = TypeName;
                switch (ArrayKind)
                {
                    case ArrayType.Array:
                        typeLabel += "[";
                        if (ArraySize != null)
                            typeLabel += ArraySize.ToString();
                        typeLabel += "]";
                        break;
                    case ArrayType.List:
                        typeLabel = "List<" + typeLabel + ">";
                        break;
                }

                return l + ElementName + " : " + typeLabel;
            }
            set
            {
                ArrayType? arrayKind = null;
                int? arraySize = null;
                UmlModifier m;
                value = value.Trim();
                if (value.Length > 1)
                {
                    string s = value.Substring(0, 1);
                    if (s == "-") m = UmlModifier._private;
                    else if (s == "#") m = UmlModifier._protected;
                    else if (s == "+") m = UmlModifier._public;
                    else
                    {
                        m = UmlModifier._public;
                        value = s + value;
                    }
                    value = value.Substring(1).Trim();

                    if (value.Length > 0)
                    {
                        int i = value.IndexOf(':');
                        string type = null;
                        if (i >= 0)
                        {
                            type = value.Substring(i + 1).Trim();
                            if (isArray(type))
                                arrayKind = getArrayKind(ref type, ref arraySize);
                            value = value.Substring(0, i).Trim();
                        }
                        if (value.Length > 0)
                        {
                            Modifiers = m;
                            TypeName = type ?? "string";
                            if (value.StartsWith("/"))
                                value = value.Substring(1);
                            ElementName = value;
                            ArrayKind = arrayKind;
                            ArraySize = arraySize;
                        }
                    }
                }
                OnPropertyChanged("ElementLabel");
            }

        }

        private int? _arraySize;
        public int? ArraySize
        {
            get { return _arraySize; }
            set
            {
                _arraySize = value;
                OnPropertyChanged("ArraySize");
                OnPropertyChanged("ElementLabel");
                EventService.BroadcastEvent(this, UmlProperty.Events.CardinalityModified);
            }
        }

        private ArrayType? _arrayKind;
        public ArrayType? ArrayKind
        {
            get { return _arrayKind; }
            set
            {
                _arrayKind = value;
                OnPropertyChanged("ArrayKind");
                OnPropertyChanged("ElementLabel");
                EventService.BroadcastEvent(this, UmlProperty.Events.CardinalityModified);
            }
        }

        private string _typeName;
        public override string TypeName
        {
            get { return _typeName; }
            set
            {
                value = value.Trim();
                UmlElement e = UmlModel.CurrentDiagram.GetElementByName(value);
                if (e != null)
                    value = e.Name;
                if (value.Length > 0) _typeName = value;
                OnPropertyChanged("TypeName");
                OnPropertyChanged("ElementLabel");
                EventService.BroadcastEvent(this, UmlProperty.Events.Modified);
            }
        }

        private string _derivationExpression = "";
        public string DerivationExpression
        {
            get { return _derivationExpression; }
            set
            {
                _derivationExpression = value.Trim();
                OnPropertyChanged("DerivationExpression");
                OnPropertyChanged("ElementLabel");
            }
        }

        private string _customGetter = "";
        public string CustomGetter
        {
            get { return _customGetter; }
            set
            {
                _customGetter = value;
                OnPropertyChanged("CustomGetter");
            }
        }

        private string _customSetter = "";
        public string CustomSetter
        {
            get { return _customSetter; }
            set
            {
                _customSetter = value;
                OnPropertyChanged("CustomSetter");
            }
        }

        private UmlModifier _getterModifiers = UmlModifier._public;
        public UmlModifier GetterModifiers
        {
            get { return _getterModifiers; }
            set {
                _getterModifiers = value;
                OnPropertyChanged("GetterModifier");
            }
        }

        private UmlModifier _setterModifiers = UmlModifier._public;
        public UmlModifier SetterModifiers
        {
            get { return _setterModifiers; }
            set
            {
                _setterModifiers = value;
                OnPropertyChanged("SetterModifier");
            }
        }

        public UmlProperty(string name, string type, UmlModifier modifier)
        {
            EventService.StopBroadcasting(this);
            this.ElementName = name;
            this.Modifiers = modifier;                     
            this.TypeName = type;
            EventService.ResumeBroadcasting(this);
        }
        public UmlProperty(string name, UmlModifier modifier)
            : this(name, "String", modifier)
        {
        }
        public UmlProperty(string name)
            : this(name, UmlModifier._public)
        {
        }
        private UmlProperty()
        {
        }

        public UmlProperty(MemberInfo member):this(member.Name)
        {
            switch (member.MemberType)
                {
                    case MemberTypes.Property:
                        PropertyInfo pInfo = (PropertyInfo)member;
                        this.TypeName = AssemblyLoader.GetTypeName(pInfo.PropertyType);
                        MethodInfo getter = pInfo.GetGetMethod(true);
                        MethodInfo setter = pInfo.GetSetMethod(true);
                        if (getter != null) GetterModifiers = UmlModifierItem.GetUmlModifiers(getter.Attributes);
                        if (setter != null) SetterModifiers = UmlModifierItem.GetUmlModifiers(setter.Attributes);
                        break;
                    case MemberTypes.Field:
                        FieldInfo fInfo = (FieldInfo)member;
                        this.TypeName = AssemblyLoader.GetTypeName(fInfo.FieldType);
                        Modifiers = UmlModifierItem.GetUmlModifiers(fInfo.Attributes);
                        break;
                }
        }

        public void CopyFrom(UmlProperty from)
        {
            // should be a call to some reflection based properties copy method...

            ElementName = from.ElementName;
            Modifiers = from.Modifiers;
            TypeName = from.TypeName;
            StereoType = from.StereoType;
            DerivationExpression = from.DerivationExpression;
            CustomGetter = from.CustomGetter;
            CustomSetter = from.CustomSetter;
        }

/*        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion*/

        #region ICloneable Members

        public object Clone()
        {
            return this.MemberwiseClone();
        }

        public override ObservableCollection<UmlElement> GetChilds()
        {
            ObservableCollection<UmlElement> elements = new ObservableCollection<UmlElement>();
            UmlElement element = null;//UmlElementList.Instance.GetElementByName(this.TypeName);
            if (element != null)
                elements.Add(element);
            return elements;
        }

        #endregion
    }
}
